﻿module Dom

open StyleSheets
open ScriptingExposedAttribute
open Uris

type Event(eventType:string) = 
    member this.``type`` = eventType

type MouseEvent(eventType:string, x:int, y:int) = 
    inherit Event(eventType)
    let _x = x
    let _y = y

type KeyboardEvent(eventType:string, char:System.Char) = 
    inherit Event(eventType)
    member this.char = char


type [<AbstractClass>] Node () = 
    let mutable _parentNode : ParentNode option = None
    member this.parentNode
        with get() = 
            match _parentNode with
            | None -> failwith "!"
            | Some elem -> elem
        and set(parentNode) =
            _parentNode <- Some parentNode

    member this.parentElement
        with get() =
            match this.parentNode with
            | :? Element as elem -> Some elem
            | _ -> None

    abstract member ownerDocument : Document

    abstract member redraw : unit -> unit
    default this.redraw() = this.ownerDocument.redraw()
            

and [<AbstractClass>] Element (doc, tagName:string, attributes:Map<string,string>) = 
    inherit ParentNode()
   
    override this.ownerDocument = doc
    member this.tagName = tagName
    member this.attributes = attributes
    member this.attributeOrDefault(name, deflt) =
        match this.attributes.TryFind name with
        | None -> deflt
        | Some x -> x

    abstract member isFocusable : bool


and [<AbstractClass>] ParentNode () =
    inherit Node()    
    let mutable _children = System.Collections.Generic.List<Node>()
    member this.childNodes = _children

    member this.appendChild(node:Node) =
        node.parentNode <- this
        do this.ownerDocument.onAdd(node) 
        do this.onAdd(node)
        do this.childNodes.Add(node)
        node

    member this.childElements =
        this.childNodes |> List.ofSeq |>
            List.choose (function
                            | :? Element as elem -> Some (elem)
                            | _ -> None)

    abstract member onAdd : Node -> unit
    default this.onAdd(node:Node) = ()

    abstract member dispatchEvent : Event -> unit    


and ElementFactory = Document -> string -> Map<string,string> -> Element

and DomChangedCallback = Document->unit

and [<AbstractClass>] Document(uri:HttpUrl, elementFactory:ElementFactory, domChangedCallback:DomChangedCallback) =
    inherit ParentNode()

    let _domChangedCallback = domChangedCallback  
    let _elementFactory = elementFactory        
    let mutable _stylesheets = StylesheetSet()     

    member this.uri = uri
    member this.stylesheets = _stylesheets    

    override this.ownerDocument = this
    
    member this.createElement(tagName, ?attributes) : Element =     
        let atts = defaultArg attributes Map.empty
        elementFactory this tagName atts

    override this.redraw() = 
        do _domChangedCallback(this)

    abstract member createTextNode : string -> Text 
    default this.createTextNode(text:string) =
        new Text(this, text)

    abstract dispatchKeyboardEvent : string -> char -> unit


and Text(doc, text) = 
    inherit Node()
    let mutable _text = text
    let mutable _areas = []
    override this.ownerDocument = doc

    member this.nodeValue 
        with get() = _text
        and set(txt:string) = do 
            _text <- txt
            this.redraw()
